print("--- 加载 NEW alarm_manager.py (数据库版本) ---") # 添加版本标识

import uuid
from datetime import datetime, timedelta, date # Added date import
import json # <-- 添加 json 模块导入
# import os # Removed os import (no longer needed for paths)
import sys # Keep sys for frozen check if needed later
from PyQt6.QtCore import QObject, pyqtSignal, QTimer, QDateTime, QThread
import threading
import time

# Import the database manager and initialize the pool
from core import db_manager # <-- Use absolute import
# --- 移除错误的导入 --- 
# from core.scheduler import AlarmScheduler # <-- 添加导入
# ---------------------------

# Try to import dateutil, keep the warning
try:
    from dateutil.relativedelta import relativedelta
except ImportError:
    relativedelta = None
    print("警告: 未找到 python-dateutil 库，部分重复规则（如每月、每年）可能计算不精确。请运行 'pip install python-dateutil' 安装。")

# --- Removed old data path logic ---
# def get_base_path(): ...
# base_dir = get_base_path()
# DATA_DIR = os.path.join(base_dir, 'data')
# ALARMS_FILE = os.path.join(DATA_DIR, "alarms.json")
# CATEGORIES_FILE = os.path.join(DATA_DIR, "categories.json")
# try:
#     os.makedirs(DATA_DIR, exist_ok=True) ...
# except OSError as e: ...
# ------------------------------------

# Define defaults
DEFAULT_REPEAT_RULE_DISPLAY = "一次"
DEFAULT_CATEGORY = "工作" # Ensure this matches a potential default in DB or is added

# --- 将 Alarm 类定义移到 AlarmScheduler 之前 ---
class Alarm:
    """Represents a single alarm event."""
    # Removed to_dict and from_dict methods as they were for JSON
    def __init__(self, alarm_time: datetime | None, message: str, id: str = None, enabled: bool = True, repeat_rule: str = None, category: str = None, original_time: datetime | None = None):
        self.id = id if id else str(uuid.uuid4())
        self.alarm_time = alarm_time # Can be None if invalid in DB
        self.message = message
        # Ensure enabled is bool (DB might store as 0/1)
        self.enabled = bool(enabled) if enabled is not None else True
        self.repeat_rule = repeat_rule # None means "一次"
        # Category validation happens in manager
        self.category = category
        # Use provided original_time, fallback to alarm_time, then now() only if both are None
        self.original_time = original_time if original_time else (alarm_time if alarm_time else datetime.now())

    def __str__(self):
        status = "[启用]" if self.enabled else "[禁用]"
        repeat_val = self.repeat_rule if self.repeat_rule else DEFAULT_REPEAT_RULE_DISPLAY
        # Ensure category is not None for display
        category_val = self.category if self.category else DEFAULT_CATEGORY
        repeat_info = f" ({repeat_val})"
        cat_info = f" [{category_val}]"
        time_str = self.alarm_time.strftime('%Y-%m-%d %H:%M:%S') if self.alarm_time else "[时间无效]"
        return f"{status}{cat_info} {time_str}{repeat_info} - {self.message}"

    def reschedule(self, current_time: datetime):
        """根据重复规则重新计算下一次闹钟时间"""
        # Needs valid rule, original_time and current alarm_time to reschedule
        if not self.repeat_rule or not self.original_time or not self.alarm_time:
            return False # Cannot reschedule if essential info is missing

        next_time = self.alarm_time
        # Base calculation on the later of the original time or the last scheduled time
        calculation_base_time = max(self.alarm_time, self.original_time)

        # Ensure next_time is in the future relative to current_time
        while next_time <= current_time:
            delta = None
            if self.repeat_rule == "每天": delta = timedelta(days=1)
            elif self.repeat_rule == "每周": delta = timedelta(weeks=1)
            elif self.repeat_rule == "每月": delta = relativedelta(months=1) if relativedelta else timedelta(days=30) # Fallback if dateutil not installed
            # Add other rules like "每年" if needed
            # elif self.repeat_rule == "每年": delta = relativedelta(years=1) if relativedelta else timedelta(days=365)
            else:
                print(f"警告: 未知或不支持的重复规则 '{self.repeat_rule}' for alarm {self.id}")
                return False # Unknown rule

            if delta:
                # Important: Calculate next occurrence based on the calculation_base_time
                # to avoid drift if the check is delayed.
                # However, if next_time is already ahead of base_time due to previous loops, use that.
                start_calc = max(next_time, calculation_base_time)
                try:
                    # Ensure start_calc is datetime before adding relativedelta
                    if isinstance(delta, relativedelta) and isinstance(start_calc, date) and not isinstance(start_calc, datetime):
                         # Use original time component if available, otherwise midnight
                         time_comp = self.original_time.time() if self.original_time else datetime.min.time()
                         start_calc = datetime.combine(start_calc, time_comp)

                    next_time = start_calc + delta
                except TypeError as e:
                     print(f"Error calculating next time for {self.id}: {e}. Start: {start_calc}, Delta: {delta}")
                     return False # Stop if calculation fails

            else: return False # Should not happen if rule is known, but safeguard

        if next_time > self.alarm_time: # Only update if time changed
             self.alarm_time = next_time
             print(f"闹钟 {self.id} ({self.message}) 已重新调度至: {self.alarm_time}")
             return True
        else:
             # This might happen if the calculated next_time is not actually later
             # (e.g., edge cases with DST or complex rules). Treat as non-repeating for now.
             print(f"闹钟 {self.id} ({self.message}) 重新调度计算未产生未来时间，将禁用重复。")
             self.repeat_rule = None # Stop rescheduling
             return False
# ---------------------------------------------------

# --- AlarmScheduler 定义现在在 Alarm 之后 ---
class AlarmScheduler(QThread):
    """Handles alarm checking in a separate thread to avoid blocking UI."""
    # Signal to indicate an alarm is due, passing the Alarm object
    alarm_due = pyqtSignal(Alarm)

    def __init__(self, check_interval_ms=1000, parent=None):
        super().__init__(parent)
        self._alarms = {}
        self._check_interval_ms = check_interval_ms
        self._running = False
        self._lock = threading.Lock() # For thread-safe access to alarms

    def run(self):
        """The main loop for the scheduler thread."""
        print("闹钟调度线程启动...")
        self._running = True
        while self._running:
            now = datetime.now()
            alarms_to_check = []
            with self._lock:
                # Get a snapshot of alarms to check
                alarms_to_check = list(self._alarms.values())

            triggered_ids = set()
            for alarm in alarms_to_check:
                if not alarm or alarm.id in triggered_ids:
                    continue
                if alarm.enabled and alarm.alarm_time and alarm.alarm_time <= now:
                    print(f"调度器检测到闹钟触发: {alarm}")
                    self.alarm_due.emit(alarm) # Emit signal
                    triggered_ids.add(alarm.id)

            # Wait for the next check interval
            # Use a loop with shorter sleeps to allow faster stopping
            end_time = time.time() + self._check_interval_ms / 1000.0
            while self._running and time.time() < end_time:
                time.sleep(0.1) # Sleep briefly

        print("闹钟调度线程停止。")

    def stop(self):
        """Signals the scheduler thread to stop."""
        print("请求停止闹钟调度线程...")
        self._running = False

    def update_alarms(self, alarms: dict[str, Alarm]):
        """Update the list of alarms the scheduler monitors."""
        with self._lock:
            # Create a copy to avoid modifying the original dict directly
            self._alarms = dict(alarms)
            # print(f"调度器已更新闹钟列表: {len(self._alarms)} 个") # Debug
# ---------------------------------------------------

class AlarmManager(QObject):
    """Manages the collection of alarms and checks for triggers using a database."""
    alarms_changed = pyqtSignal()
    # --- 恢复 alarm_triggered 信号定义 ---
    alarm_triggered = pyqtSignal(Alarm) # 需要此信号来通知 UI
    # ----------------------------------------
    categories_changed = pyqtSignal(list)

    def __init__(self, parent=None):
        print("--- 初始化 NEW AlarmManager (数据库版本) ---")
        # --- 首先调用父类的 __init__ --- 
        super().__init__(parent)
        # -----------------------------

        self._alarms = {} # Initialize early
        self._categories = [] # Initialize early

        # --- 先初始化调度器 --- 
        # self.scheduler = AlarmScheduler(self.trigger_alarm) # 旧方式
        self.scheduler = AlarmScheduler() # 创建调度器实例
        self.scheduler.alarm_due.connect(self._handle_alarm_due) # 连接信号到处理函数
        self.scheduler.start() # 启动线程
        # -----------------------

        self._db_available = self._check_db_connection() # Check DB availability

        # 确保表存在并加载数据 (仅当 DB 可用时)
        if self._db_available:
            self._ensure_table_exists()
            self._load_categories() # 加载分类
            self._load_alarms() # 加载闹钟 (现在调用 update_scheduler_alarms 时 scheduler 已存在)
        else:
            print("警告: 数据库连接不可用，无法初始化表或加载数据。")
            # Ensure default category exists in memory even if DB fails
            if DEFAULT_CATEGORY not in self._categories:
                 self._categories.append(DEFAULT_CATEGORY)
            self.update_scheduler_alarms() # 即使DB失败，也用空列表更新调度器

        # --- 移除旧的调用和调度器初始化位置 --- 
        # self._ensure_table_exists()
        # self._load_alarms_from_db() # 数据库加载逻辑
        # self.scheduler = AlarmScheduler() # 创建调度器实例
        # self.scheduler.alarm_due.connect(self._handle_alarm_due) # 连接信号到处理函数
        # self.scheduler.start() # 启动线程
        # self.update_scheduler_alarms() # 初始加载闹钟到调度器
        # --------------------------------

        # --- 移除旧的属性初始化和定时器 --- 
        # self._alarms = {}
        # self._categories = [] # Loaded from DB
        # Timer to check for due alarms
        # self._check_timer = QTimer(self)
        # self._check_timer.timeout.connect(self._check_alarms)
        # self._check_timer.start(1000) # Check every second
        # --------------------------------

    def _check_db_connection(self):
        """尝试执行简单查询以检查数据库连接。"""
        print("--- 检查数据库连接... ---")
        try:
            # Use fetch_one=True to actually execute and get a result
            result = db_manager.execute_query("SELECT 1", fetch_one=True)
            if result is not None: # Check if query returned something
                print("--- 数据库连接检查成功。 ---")
                return True
            else:
                # This might happen if execute_query returns None due to an error within it
                print("--- 数据库连接检查失败 (查询未返回结果)。 ---")
                return False
        except Exception as e:
            # Catch potential exceptions during the query itself
            print(f"--- 数据库连接检查异常: {e} ---")
            return False

    def _ensure_table_exists(self):
        """确保数据库中存在 alarms 和 categories 表。"""
        print("--- 确保数据库表存在... ---")
        try:
            # Categories Table (Primary Key)
            query_categories = """
            CREATE TABLE IF NOT EXISTS categories (
                name VARCHAR(255) PRIMARY KEY
            )
            """
            if not db_manager.execute_update(query_categories):
                print("错误: 创建或检查 'categories' 表失败。")
                # Potentially set self._db_available to False here? Or handle upstream.
                return # Stop if category table fails
            else:
                 print("'categories' 表已存在或已创建。")

            # Alarms Table (Primary Key, Foreign Key)
            # Note: Using TEXT for message, VARCHAR for others
            # Storing enabled as TINYINT(1) for boolean
            # DATETIME for timestamps
            query_alarms = """
            CREATE TABLE IF NOT EXISTS alarms (
                id VARCHAR(36) PRIMARY KEY,
                alarm_time DATETIME NULL,
                message TEXT,
                enabled TINYINT(1) DEFAULT 1,
                repeat_rule VARCHAR(50) NULL,
                category VARCHAR(255) NULL,
                original_time DATETIME NULL,
                FOREIGN KEY (category) REFERENCES categories(name) ON DELETE SET NULL ON UPDATE CASCADE
            )
            """
            # Note on Foreign Key:
            # ON DELETE SET NULL: If a category is deleted, set alarm's category to NULL.
            # ON UPDATE CASCADE: If a category name is updated, update the alarm's category too.
            # Alternatively, ON DELETE RESTRICT prevents deleting a category if alarms use it.
            # Or ON DELETE CASCADE deletes alarms if their category is deleted.
            # SET NULL + CASCADE for rename seems reasonable here.

            if not db_manager.execute_update(query_alarms):
                print("错误: 创建或检查 'alarms' 表失败。")
            else:
                 print("'alarms' 表已存在或已创建。")

        except Exception as e:
            print(f"检查或创建数据库表时发生未知错误: {e}")

    def _load_categories(self):
        """Load categories from the database."""
        print("从数据库加载分类...")
        query = "SELECT name FROM categories ORDER BY name ASC"
        results = db_manager.execute_query(query, fetch_all=True)
        # Always start with an empty list before loading
        self._categories = []
        if results is not None:
            self._categories = [row['name'] for row in results]
            print(f"加载了 {len(self._categories)} 个分类: {self._categories}")
        else:
            print("加载分类失败或分类表为空。")
            # self._categories remains empty

        # Ensure DEFAULT_CATEGORY exists in DB and memory
        if DEFAULT_CATEGORY not in self._categories:
            print(f"默认分类 '{DEFAULT_CATEGORY}' 不在数据库中，尝试添加...")
            # Use add_category which handles both DB and memory update
            if not self.add_category(DEFAULT_CATEGORY): # add_category now returns bool
                 # If adding failed, ensure it's in memory list for UI consistency
                 if DEFAULT_CATEGORY not in self._categories:
                      print(f"警告: 添加默认分类 '{DEFAULT_CATEGORY}' 到数据库失败，但会保留在内存中。")
                      self._categories.insert(0, DEFAULT_CATEGORY) # Add to front
        # Just in case add_category succeeded but list is somehow out of sync (unlikely)
        if DEFAULT_CATEGORY not in self._categories:
             self._categories.insert(0, DEFAULT_CATEGORY)

        # Emit signal after categories are potentially modified
        self.categories_changed.emit(self._categories)


    def _load_alarms(self):
        """Load alarms from the database."""
        print("从数据库加载闹钟...")
        query = "SELECT * FROM alarms"
        results = db_manager.execute_query(query, fetch_all=True)
        self._alarms = {} # Clear existing alarms in memory first
        if results is not None:
            loaded_count = 0
            # Ensure categories are loaded before validating alarm categories
            if not self._categories:
                 print("警告: 尝试加载闹钟，但分类列表为空。将无法正确校验分类。")
                 # Attempt to load categories again if empty?
                 # self._load_categories() # Be careful about potential infinite loops

            for row in results:
                try:
                    # Validate category - if it's not in our loaded list, use default
                    category = row.get('category')
                    # Check against the in-memory self._categories list
                    if category not in self._categories:
                        if category: # Only warn if it was something other than None/empty
                             print(f"警告: 闹钟 {row.get('id')} 的分类 '{category}' 不存在于当前分类列表，将使用 '{DEFAULT_CATEGORY}'。")
                        category = DEFAULT_CATEGORY
                    # If category is None/empty after DB read, also use default
                    elif not category:
                         category = DEFAULT_CATEGORY


                    # Create Alarm object directly from DB row (dictionary)
                    alarm = Alarm(
                        id=row.get('id'),
                        alarm_time=row.get('alarm_time'), # mysql-connector should return datetime objects
                        message=row.get('message'),
                        enabled=row.get('enabled', True), # DB stores 0/1
                        repeat_rule=row.get('repeat_rule'),
                        category=category, # Use validated category
                        original_time=row.get('original_time')
                    )
                    self._alarms[alarm.id] = alarm
                    loaded_count += 1
                except Exception as e:
                    print(f"加载闹钟数据时出错: {row}, 错误: {e}")
            print(f"成功加载 {loaded_count} 个闹钟。")
        else:
            print("加载闹钟失败或闹钟表为空。")
            self._alarms = {}

        self.alarms_changed.emit()
        self.update_scheduler_alarms() # <<< 添加调用


    # Remove _save_alarms and _save_categories methods
    # def _save_alarms(self): ...
    # def _save_categories(self): ...


    def add_alarm(self, alarm: Alarm):
        """Add a new alarm to the database and memory."""
        if not self._db_available:
            print("错误：数据库不可用，无法添加闹钟。")
            return False
        # Basic check for duplicate ID in memory (though DB primary key handles actual enforcement)
        if alarm.id in self._alarms:
            print(f"警告: 尝试添加重复 ID {alarm.id} (内存中已存在)")
            # Let DB handle the primary key constraint violation if it occurs
            # return False

        # Validate category
        if not alarm.category or alarm.category not in self._categories:
            if alarm.category:
                print(f"警告: 添加闹钟时分类 '{alarm.category}' 无效或不存在，重置为 '{DEFAULT_CATEGORY}'.")
            alarm.category = DEFAULT_CATEGORY

        query = """
            INSERT INTO alarms (id, alarm_time, message, enabled, repeat_rule, category, original_time)
            VALUES (%s, %s, %s, %s, %s, %s, %s)
        """
        params = (
            alarm.id,
            alarm.alarm_time,
            alarm.message,
            1 if alarm.enabled else 0,
            alarm.repeat_rule,
            alarm.category,
            alarm.original_time
        )

        if db_manager.execute_update(query, params):
            self._alarms[alarm.id] = alarm # Add to memory only if DB insert succeeds
            self.alarms_changed.emit()
            self.update_scheduler_alarms() # <<< 添加调用
            print(f"闹钟 {alarm.id} 已成功添加到数据库。")
            return True
        else:
            print(f"错误: 添加闹钟 {alarm.id} 到数据库失败。")
            # Don't add to memory if DB failed
            return False

    def remove_alarm(self, alarm_id: str):
        """Remove an alarm by its ID from the database and memory."""
        if not self._db_available:
            print("错误：数据库不可用，无法删除闹钟。")
            return False
        # Check memory first, but proceed to DB delete even if not found in memory (consistency)
        in_memory = alarm_id in self._alarms
        if not in_memory:
             print(f"警告: 尝试删除内存中不存在的闹钟 ID {alarm_id}。仍将尝试从数据库删除。")

        query = "DELETE FROM alarms WHERE id = %s"
        params = (alarm_id,)

        if db_manager.execute_update(query, params):
            # Remove from memory only if DB delete succeeds and it was present
            if in_memory:
                del self._alarms[alarm_id]
                self.alarms_changed.emit() # Emit only if memory changed
                self.update_scheduler_alarms() # <<< 添加调用
            print(f"闹钟 {alarm_id} 已从数据库删除（或尝试删除）。")
            return True
        else:
            print(f"错误: 从数据库删除闹钟 {alarm_id} 失败。")
            return False

    def update_alarm(self, alarm: Alarm):
        """Update an existing alarm in the database and memory."""
        if not self._db_available:
            print("错误：数据库不可用，无法更新闹钟。")
            return False
        # Must exist in memory to update (avoids accidentally inserting via update)
        if alarm.id not in self._alarms:
            print(f"错误: 尝试更新内存中不存在的闹钟 ID {alarm.id}")
            return False

        # Validate category
        if not alarm.category or alarm.category not in self._categories:
            if alarm.category:
                print(f"警告: 更新闹钟 {alarm.id} 时分类 '{alarm.category}' 无效，重置为 '{DEFAULT_CATEGORY}'.")
            alarm.category = DEFAULT_CATEGORY

        # Update original_time logic: only update if alarm_time itself has changed
        stored_alarm = self._alarms.get(alarm.id) # Get current in-memory state
        if stored_alarm and stored_alarm.alarm_time != alarm.alarm_time:
             # Only update original_time if alarm_time is actually different
             if alarm.alarm_time: # Ensure the new time is not None
                  print(f"闹钟 {alarm.id} 时间已更改，同时更新 original_time。")
                  alarm.original_time = alarm.alarm_time
             else: # If new time is None, maybe keep old original_time? Or set to None?
                  print(f"警告: 闹钟 {alarm.id} 时间更新为 None，original_time 将保持不变 ({alarm.original_time})。")
                  # Keep alarm.original_time as is unless explicitly cleared
        # Use the potentially updated alarm object's original_time for the DB query
        original_time_to_save = alarm.original_time

        query = """
            UPDATE alarms SET
                alarm_time = %s,
                message = %s,
                enabled = %s,
                repeat_rule = %s,
                category = %s,
                original_time = %s
            WHERE id = %s
        """
        params = (
            alarm.alarm_time,
            alarm.message,
            1 if alarm.enabled else 0,
            alarm.repeat_rule,
            alarm.category,
            original_time_to_save, # Use the potentially updated value
            alarm.id
        )

        if db_manager.execute_update(query, params):
            self._alarms[alarm.id] = alarm # Update in memory with the new alarm object
            self.alarms_changed.emit()
            self.update_scheduler_alarms() # <<< 添加调用
            print(f"闹钟 {alarm.id} 已在数据库中更新。")
            return True
        else:
            print(f"错误: 更新数据库中的闹钟 {alarm.id} 失败。")
            # Memory remains unchanged if DB fails
            return False

    def get_alarm(self, alarm_id: str) -> Alarm | None:
        """Get an alarm by its ID from memory."""
        return self._alarms.get(alarm_id)

    def get_all_alarms(self) -> list[Alarm]:
        """Get a list of all alarms from memory, sorted by time."""
        # Ensure sorting handles None times gracefully
        return sorted(self._alarms.values(), key=lambda a: a.alarm_time if a.alarm_time else datetime.max)

    # --- 新增方法：处理来自调度器的信号 --- 
    def _handle_alarm_due(self, alarm: Alarm):
        """Handles the alarm_due signal from the AlarmScheduler."""
        print(f"AlarmManager 收到调度器信号: {alarm.id} ({alarm.message})")
        # 触发 UI 更新或其他操作
        self.alarm_triggered.emit(alarm) # Re-emit for the UI

        # --- 处理闹钟重复或禁用逻辑 (从旧的 _check_alarms 移过来) --- 
        now = datetime.now() # Get current time for rescheduling
        updates_for_db = {}
        memory_changed = False
        stored_alarm = self._alarms.get(alarm.id) # Get the alarm from manager's memory

        if not stored_alarm:
             print(f"错误: 调度器触发了内存中不存在的闹钟 {alarm.id}")
             return

        rescheduled = stored_alarm.reschedule(now)
        if rescheduled:
            updates_for_db['alarm_time'] = stored_alarm.alarm_time
            if stored_alarm.repeat_rule is not None: updates_for_db['repeat_rule'] = stored_alarm.repeat_rule
            memory_changed = True
            print(f"闹钟 {stored_alarm.id} 已重新安排 (管理器处理)。")
        else:
            if stored_alarm.repeat_rule:
                 print(f"闹钟 {stored_alarm.id} 重新调度失败，将禁用重复规则 (管理器处理)。")
                 stored_alarm.repeat_rule = None
                 updates_for_db['repeat_rule'] = None
                 memory_changed = True
            if stored_alarm.enabled:
                 stored_alarm.enabled = False
                 updates_for_db['enabled'] = 0
                 memory_changed = True
                 print(f"闹钟 {stored_alarm.id} 是一次性或重复失败，将禁用 (管理器处理)。")

        # 更新数据库
        if updates_for_db:
            set_clauses = [f"{key} = %s" for key in updates_for_db]
            params = list(updates_for_db.values())
            params.append(stored_alarm.id)
            query = f"UPDATE alarms SET {', '.join(set_clauses)} WHERE id = %s"
            if db_manager.execute_update(query, tuple(params)):
                print(f"闹钟 {stored_alarm.id} 状态/时间已在数据库中更新 (管理器处理)。")
            else:
                print(f"警告: 更新数据库中的闹钟 {stored_alarm.id} 失败 (管理器处理)。")
        
        # 如果内存状态改变，则通知 UI
        if memory_changed:
            self.alarms_changed.emit()
            self.update_scheduler_alarms() # 更新调度器中的闹钟列表
        # ------------------------------------------------------

    # --- 新增方法：更新调度器中的闹钟列表 --- 
    def update_scheduler_alarms(self):
         """将当前内存中的闹钟列表发送给调度器线程。"""
         print("向调度器发送更新后的闹钟列表...")
         self.scheduler.update_alarms(self._alarms)
    # ----------------------------------------

    # --- 分类管理方法 (Database backed) ---

    def get_categories(self) -> list[str]:
        """Get the list of categories from memory."""
        return self._categories[:] # Return a copy

    def add_category(self, category: str) -> bool:
        """Add a new category to the database and memory."""
        if not self._db_available:
            print("错误：数据库不可用，无法添加分类。")
            return False
        if not category:
             print("错误: 尝试添加空分类名称。")
             return False
        # Check case-insensitively? Assuming DB is case-sensitive for PK. Check memory case-sensitively.
        if category in self._categories:
            print(f"信息: 分类 '{category}' 已存在。")
            return False # Indicate not added

        query = "INSERT INTO categories (name) VALUES (%s)"
        params = (category,)

        if db_manager.execute_update(query, params):
            if category not in self._categories: # Add only if truly new
                 self._categories.append(category)
                 self._categories.sort() # Keep it sorted
                 self.categories_changed.emit(self._categories)
            print(f"分类 '{category}' 已成功添加到数据库。")
            return True
        else:
            print(f"错误: 添加分类 '{category}' 到数据库失败。")
            return False

    def remove_category(self, category: str) -> bool:
        """Remove a category, reassigning its alarms to the default category."""
        if not self._db_available:
            print("错误：数据库不可用，无法删除分类。")
            return False
        if not category or category == DEFAULT_CATEGORY:
            print(f"错误: 无法删除空分类或默认分类 '{DEFAULT_CATEGORY}'。")
            return False
        # Check memory first
        if category not in self._categories:
            print(f"错误: 尝试删除内存中不存在的分类 '{category}'。")
            # Optionally, try deleting from DB anyway for consistency? Risky.
            return False

        print(f"准备删除分类 '{category}' 并将关联闹钟重置为 '{DEFAULT_CATEGORY}'...")

        # Step 1: Update alarms in the database
        update_query = "UPDATE alarms SET category = %s WHERE category = %s"
        update_params = (DEFAULT_CATEGORY, category)
        # We proceed even if update fails, but log it. Category delete is main goal.
        if not db_manager.execute_update(update_query, update_params):
            print(f"警告: 更新数据库中属于 '{category}' 的闹钟失败。将继续尝试删除分类。")
        else:
             print(f"数据库中属于 '{category}' 的闹钟已更新为 '{DEFAULT_CATEGORY}'。")

        # Step 2: Delete the category from the database
        delete_query = "DELETE FROM categories WHERE name = %s"
        delete_params = (category,)
        if not db_manager.execute_update(delete_query, delete_params):
            print(f"错误: 从数据库删除分类 '{category}' 失败。")
            # Rollback alarm updates? Difficult state. Return failure.
            return False
        else:
             print(f"分类 '{category}' 已从数据库删除。")

        # Step 3: Update memory
        if category in self._categories:
            self._categories.remove(category)
        else: # Should have been caught earlier, but log just in case
             print(f"警告: 分类 '{category}' 在数据库中删除成功，但在内存列表中未找到？")

        # Update alarms in memory as well
        alarms_updated_in_memory = False
        for alarm in self._alarms.values():
            if alarm.category == category:
                alarm.category = DEFAULT_CATEGORY
                alarms_updated_in_memory = True

        self.categories_changed.emit(self._categories)
        # Also emit alarms changed if any alarms were reassigned in memory
        if alarms_updated_in_memory:
             self.alarms_changed.emit()

        print(f"分类 '{category}' 已成功删除，关联闹钟已重置。")
        return True


    def rename_category(self, old_name: str, new_name: str) -> bool:
        """Rename a category in the database (alarms updated by cascade) and memory."""
        if not self._db_available:
            print("错误：数据库不可用，无法重命名分类。")
            return False

        # --- Validation ---
        if not old_name or old_name == DEFAULT_CATEGORY:
            print(f"错误: 无法重命名空分类或默认分类 '{DEFAULT_CATEGORY}'。")
            return False
        if not new_name or new_name == DEFAULT_CATEGORY:
            print(f"错误: 新分类名称不能为空或是默认分类 '{DEFAULT_CATEGORY}'。")
            return False
        if old_name not in self._categories:
            print(f"错误: 原始分类 '{old_name}' 不存在。")
            return False
        if new_name in self._categories:
            print(f"错误: 新分类名称 '{new_name}' 已存在。")
            return False
        # -----------------

        print(f"准备将分类 '{old_name}' 重命名为 '{new_name}'...")

        # Step 1: Update the category name in the database
        query = "UPDATE categories SET name = %s WHERE name = %s"
        params = (new_name, old_name)

        if not db_manager.execute_update(query, params):
            print(f"错误: 在数据库中重命名分类 '{old_name}' -> '{new_name}' 失败。")
            return False
        else:
            print(f"分类 '{old_name}' 已在数据库中成功重命名为 '{new_name}'。级联更新应已处理闹钟。")

        # Step 2: Update memory
        try:
            index = self._categories.index(old_name)
            self._categories[index] = new_name
            self._categories.sort() # Keep sorted
        except ValueError:
             print(f"警告: '{old_name}' 在数据库中更新成功，但在内存分类列表中未找到？")
             if new_name not in self._categories: self._categories.append(new_name)
             self._categories.sort()


        alarms_updated_in_memory = False
        for alarm in self._alarms.values():
            if alarm.category == old_name:
                alarm.category = new_name
                alarms_updated_in_memory = True

        self.categories_changed.emit(self._categories)
        if alarms_updated_in_memory:
             self.alarms_changed.emit()

        print(f"分类 '{old_name}' 已在内存中重命名为 '{new_name}'。")
        return True


    # --- 实现导入功能 ---
    def import_alarms_from_json(self, filepath: str) -> int | None:
        """从 JSON 文件导入闹钟到数据库。

        Args:
            filepath: JSON 文件的路径。

        Returns:
            成功导入的闹钟数量，如果发生严重错误则返回 None。
        """
        if not self._db_available:
            print("错误：数据库不可用，无法导入闹钟。")
            return None
        
        print(f"开始从 JSON 文件导入闹钟: {filepath}")
        imported_count = 0
        failed_count = 0

        try:
            with open(filepath, 'r', encoding='utf-8') as f:
                data = json.load(f)
        except FileNotFoundError:
            print(f"导入错误: 文件未找到 {filepath}")
            # UI 层会处理 FileNotFoundError，这里返回 None 表示失败
            return None
        except json.JSONDecodeError as e:
            print(f"导入错误: JSON 文件格式无效: {e}")
            return None
        except Exception as e:
             print(f"导入错误: 读取文件时发生未知错误: {e}")
             return None

        if not isinstance(data, list):
            print("导入错误: JSON 文件的顶层结构必须是一个列表 [ ]。")
            return None

        print(f"从文件中解析出 {len(data)} 条记录。开始逐条导入...")
        # 先加载一次当前分类，用于验证
        current_categories = self.get_categories() 

        for alarm_data in data:
            if not isinstance(alarm_data, dict):
                print(f"跳过无效的记录 (非字典): {alarm_data}")
                failed_count += 1
                continue
            
            try:
                # --- 数据验证和转换 --- 
                alarm_time_str = alarm_data.get('alarm_time')
                alarm_time = None
                if alarm_time_str:
                    try:
                        # 尝试多种可能的日期时间格式
                        alarm_time = datetime.fromisoformat(alarm_time_str) 
                    except ValueError:
                        try:
                           alarm_time = datetime.strptime(alarm_time_str, '%Y-%m-%d %H:%M:%S')
                        except ValueError:
                             print(f"警告: 跳过记录，无效的 alarm_time 格式: {alarm_time_str}")
                             failed_count += 1
                             continue # 跳过这条记录

                message = alarm_data.get('message')
                if not message:
                    print(f"警告: 跳过记录，缺少 message 字段。")
                    failed_count += 1
                    continue
                    
                # ID (可选，如果不存在则生成)
                alarm_id = alarm_data.get('id') 

                # Enabled (布尔值，默认为 True)
                enabled = alarm_data.get('enabled', True)
                if not isinstance(enabled, bool):
                     enabled = str(enabled).lower() in ['true', '1', 'yes'] # 简单的转换

                # Repeat Rule (字符串，可选)
                repeat_rule = alarm_data.get('repeat_rule')
                # 可选：验证 repeat_rule 是否为有效值
                # valid_rules = ["每天", "每周", "每月", None]
                # if repeat_rule not in valid_rules:
                #      print(f"警告: 记录 {alarm_id or message} 存在无效的 repeat_rule '{repeat_rule}'，将忽略重复。")
                #      repeat_rule = None

                # Category (字符串，可选，验证是否存在，不存在则使用默认)
                category = alarm_data.get('category')
                if not category or category not in current_categories:
                    if category:
                         print(f"警告: 记录 {alarm_id or message} 的分类 '{category}' 不存在，将使用默认分类 '{DEFAULT_CATEGORY}'")
                    category = DEFAULT_CATEGORY
                
                # Original Time (可选)
                original_time_str = alarm_data.get('original_time')
                original_time = None
                if original_time_str:
                     try:
                         original_time = datetime.fromisoformat(original_time_str)
                     except ValueError:
                         try:
                             original_time = datetime.strptime(original_time_str, '%Y-%m-%d %H:%M:%S')
                         except ValueError:
                              print(f"警告: 记录 {alarm_id or message} 的 original_time 格式无效 '{original_time_str}'，将忽略。")
                              original_time = None # 忽略无效的原始时间
                
                # 如果原始时间为空，但闹钟时间有效，则用闹钟时间作为原始时间
                if not original_time and alarm_time:
                    original_time = alarm_time

                # --- 创建 Alarm 对象并添加 --- 
                new_alarm = Alarm(
                    alarm_time=alarm_time,
                    message=message,
                    id=alarm_id, # 如果是 None，Alarm 会自己生成
                    enabled=enabled,
                    repeat_rule=repeat_rule,
                    category=category,
                    original_time=original_time
                )

                # 使用 add_alarm 添加，它会处理数据库插入和内存更新
                if self.add_alarm(new_alarm):
                    imported_count += 1
                else:
                    # add_alarm 内部会打印失败原因
                    failed_count += 1
                    # 是否要在这里停止导入？取决于需求。目前继续导入下一条。

            except Exception as e:
                print(f"处理导入记录时发生错误: {alarm_data}, 错误: {e}")
                failed_count += 1

        print(f"导入处理完成。成功导入: {imported_count}，失败/跳过: {failed_count}")
        return imported_count
    # ----------------------

    # --- 实现导出功能 ---
    def export_alarms_to_json(self, filepath: str) -> int | None:
        """将当前内存中的所有闹钟导出到 JSON 文件。

        Args:
            filepath: 要保存的 JSON 文件的路径。

        Returns:
            成功导出的闹钟数量，如果发生错误则返回 None。
        """
        # 注意：我们导出的是当前内存中的状态，而不是直接从数据库读取
        # 这确保了导出的数据与用户在界面上看到的一致
        print(f"开始导出闹钟到 JSON 文件: {filepath}")
        alarms_to_export = self.get_all_alarms() # 从内存获取
        export_data = []
        exported_count = 0

        for alarm in alarms_to_export:
            try:
                alarm_dict = {
                    "id": alarm.id,
                    # 将 datetime 对象转换为 ISO 格式字符串，处理 None 值
                    "alarm_time": alarm.alarm_time.isoformat() if alarm.alarm_time else None,
                    "message": alarm.message,
                    "enabled": alarm.enabled,
                    "repeat_rule": alarm.repeat_rule,
                    "category": alarm.category,
                    "original_time": alarm.original_time.isoformat() if alarm.original_time else None
                }
                export_data.append(alarm_dict)
                exported_count += 1
            except Exception as e:
                 print(f"导出闹钟 {alarm.id} 时准备数据出错: {e}")
                 # 选择跳过这个闹钟还是中止导出？这里选择跳过
                 continue 

        if not export_data:
            print("没有可导出的闹钟。")
            # 返回 0 表示没有导出任何内容，但操作本身不算失败
            # 如果希望提示用户"无内容可导出"，可以在 UI 层处理返回值为 0 的情况
            # return 0 

        try:
            with open(filepath, 'w', encoding='utf-8') as f:
                # 使用 indent 使 JSON 文件更易读
                json.dump(export_data, f, ensure_ascii=False, indent=4)
            print(f"成功导出 {exported_count} 个闹钟到 {filepath}")
            return exported_count
        except IOError as e:
            print(f"导出错误: 写入文件时发生 IO 错误: {e}")
            return None
        except Exception as e:
            print(f"导出错误: 写入 JSON 时发生未知错误: {e}")
            return None
    # ----------------------

    # --- Remove Import/Export methods --- (旧的占位符)
    # def import_alarms_from_file(self, filepath: str) -> int: ...
    # def export_alarms_to_file(self, filepath: str) -> int: ...

# Example Usage (for testing, might need adjustment)
if __name__ == '__main__':
    # This test block needs QObject loop or similar to run timer
    print("运行 AlarmManager 测试 (数据库版本)...") # 添加版本标识
    # Need a way to keep the script running to test timer, e.g., QApplication
    # from PyQt6.QtWidgets import QApplication
    # app = QApplication(sys.argv) # Basic app loop for timer

    manager = AlarmManager()

    if manager._db_available:
        print(f"数据库连接成功。")
        print(f"当前分类: {manager.get_categories()}")
        print(f"当前闹钟 ({len(manager.get_all_alarms())}):")
        for alm in manager.get_all_alarms():
             print(f"- {alm}")

        # Example: Add a test alarm (requires running event loop)
        # test_time = datetime.now() + timedelta(seconds=10)
        # test_alarm = Alarm(test_time, "数据库测试闹钟!", category="工作") # Assume "工作" exists
        # manager.add_alarm(test_alarm)

        # print("\n等待闹钟触发 (需要事件循环)...")
        # sys.exit(app.exec()) # Start event loop if using QApplication
    else:
        print("数据库未连接，无法执行测试。")

    # Keep running for a bit if no event loop
    # import time
    # print("测试结束。")
    # try:
    #      print("运行测试循环 (按 Ctrl+C 退出)...")
    #      while True:
    #          time.sleep(1)
    # except KeyboardInterrupt:
    #      print("测试退出。")
